﻿using pdfer.FileObjects;
using pdfer.Syntax;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace pdfer.Filters
{
    /// <summary>
    /// 预测参数
    /// </summary>
    internal class PredictorParam
    {
        private Dictionary<String, Object> dic;

        public PredictorParam(PdfFileObject pdfFileObject)
        {
            if(pdfFileObject.TryGetValue(PdfName.Key_DecodeParms, out var obj) 
                || pdfFileObject.TryGetValue(PdfName.Key_Dp, out obj))
            {
                if (obj == null)
                {
                    return;
                }
                dic = PdfParserUtils.ParseDictionary(obj, pdfFileObject);
                Init(pdfFileObject);
            }
        }

        private void Init(PdfFileObject pdfFileObject)
        {
            if(dic.TryGetValue(PdfName.Key_Predictor, out var obj3))
            {
                Predictor = PdfParserUtils.ParseInt32(obj3,pdfFileObject);
            }
            if (dic.TryGetValue(PdfName.Key_Colors, out obj3))
            {
                Colors = PdfParserUtils.ParseInt32(obj3, pdfFileObject);
            }
            if (dic.TryGetValue(PdfName.Key_BitsPerComponent, out obj3))
            {
                BitsPerComponent = PdfParserUtils.ParseInt32(obj3, pdfFileObject);
            }
            if (dic.TryGetValue(PdfName.Key_Columns, out obj3))
            {
                Columns = PdfParserUtils.ParseInt32(obj3, pdfFileObject);
            }
            if (dic.TryGetValue(PdfName.Key_EarlyChange, out obj3))
            {
                EarlyChange = PdfParserUtils.ParseInt32(obj3, pdfFileObject);
            }
        }

        /// <summary>
        /// 大于9时才需要处理
        /// </summary>
        public Int32 Predictor { get; set; } = 1;
        /// <summary>
        /// 1 2 3 4
        /// 每个像素的分量个数
        /// rgb 3 rgba 4 cmyk 4
        /// </summary>
        public Int32 Colors { get; set; } = 1;
        /// <summary>
        /// 1 2 4 8 16
        /// 每个分量的bit数
        /// </summary>
        public Int32 BitsPerComponent { get; set; } = 8;
        /// <summary>
        /// 每行的样本数
        /// </summary>
        public Int32 Columns { get; set; } = 1;
        /// <summary>
        /// 何时增加代码长度的指示
        /// </summary>
        public Int32 EarlyChange { get; set; } = 1;

        // 获取每个样本的字节数
        public Int32 BytePerPix
        {
            get
            {
                return Colors * BitsPerComponent / 8;
            }
        }
        // 获取每行的字节数
        public Int32 BytePreRow
        {
            get
            {
                // + 7 保证算出来的 byte 数量 整行都在一个字节
                return (Colors * BitsPerComponent * Columns + 7) / 8;
            }
        }
    }

    /// <summary>
    /// 针对LZW Flate算法解压后的数据在根据预测算法还原数据
    /// </summary>
    internal class PredictorDecodeFilter
    {

        /// <summary>
        /// FlateDecode & LZWDecode
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public Byte[] Decode(ReadOnlySpan<Byte> data, PredictorParam? predictor)
        {
            if (predictor == null || predictor.Predictor < 10)
            {
                // 1 No prediction(the default value)
                // 2 TIFF Predictor 2
                // 10 PNG prediction(on encoding, PNG None on all rows)
                // 11 PNG prediction(on encoding, PNG Sub on all rows)
                // 12 PNG prediction(on encoding, PNG Up on all rows)
                // 13 PNG prediction(on encoding, PNG Average on all rows)
                // 14 PNG prediction(on encoding, PNG Paeth on all rows)
                // 15 PNG prediction(on encoding, PNG optimum)
                return data.ToArray();
            }
            using var ms = new MemoryStream();
            ms.Write(data);
            ms.Position = 0;
            using var target = new MemoryStream(data.Length);
            var bytePrePix = predictor.BytePerPix;
            var bytePreRow = predictor.BytePreRow;

            var dataSpan = data;
            var row = new Byte[bytePreRow];
            var rowSpan = new Span<Byte>(row);
            var lastRow = new Byte[bytePreRow];
            var lastRowSpan = new Span<Byte>(lastRow);
            while (true)
            {
                rowSpan.Clear();
                /// png预测类型
                /// https://www.rfc-editor.org/rfc/rfc2083
                /// 6.1 Filter types
                var pngFilterType = ms.ReadByte();
                if(pngFilterType < 0)
                {
                    // 读完了
                    break;
                }
                var cnt = ms.Read(rowSpan);
                if(cnt < 1)
                {
                    // 读完了
                    break;
                }
                switch (pngFilterType)
                {
                    case 0:
                        // 没有预测
                        break;
                    case 1:
                        {//Sub
                            //Sub(x) = Raw(x) - Raw(x - bpp)
                            //Raw(x) = Sub(x) + Raw(x - bpp)
                            for (var idx = bytePrePix; idx < bytePreRow; idx++)
                            {
                                rowSpan[idx] += rowSpan[idx - bytePrePix];
                            }
                            break;   
                        }
                    case 2:
                        {//Up
                            // Up(x) = Raw(x) - Prior(x)
                            // Raw(x) = Up(x) + Prior(x)
                            for (var idx = 0; idx < bytePreRow; idx++)
                            {
                                rowSpan[idx] += lastRowSpan[idx];
                            }
                            break;
                        }
                    case 3:
                        {//Average
                            // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
                            // Raw(x) = Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
                            for (var idx = 0; idx < bytePrePix; idx++)
                            {
                                rowSpan[idx] += (Byte)((lastRowSpan[idx]) / 2);
                            }
                            for (var idx = bytePrePix; idx < bytePreRow; idx++)
                            {
                                rowSpan[idx] += (Byte)((lastRowSpan[idx] + rowSpan[idx - bytePrePix]) / 2);
                            }
                            break;
                        }
                    case 4:
                        {//Paeth
                            // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
                            // Raw(x) = Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
                            for (var idx = 0; idx < bytePrePix; idx++)
                            {
                                rowSpan[idx] += lastRowSpan[idx];
                            }
                            for (var idx = bytePrePix; idx < bytePreRow; idx++)
                            {
                                var left = rowSpan[idx - bytePrePix];
                                var above = lastRowSpan[idx];
                                var leftAbove = lastRowSpan[idx - bytePrePix];
                                // function PaethPredictor(a, b, c)
                                // begin
                                //      ; a = left, b = above, c = upper left
                                //      p := a + b - c      ; initial estimate
                                //      pa:= abs(p - a)     ; distances to a, b, c
                                //      pb := abs(p - b)
                                //      pc:= abs(p - c)
                                //      ; return nearest of a, b, c,
                                //      ; breaking ties in order a, b, c.
                                //      if pa <= pb AND pa <= pc then return a
                                //      else if pb <= pc then return b
                                //      else return c
                                // end
                                var p = left + above - leftAbove;
                                var pa = Math.Abs(p - left);
                                var pb = Math.Abs(p - above);
                                var pc = Math.Abs(p - leftAbove);
                                if(pa <= pb && pa <= pc)
                                {
                                    rowSpan[idx] = left;
                                }
                                else if(pb <= pc)
                                {
                                    rowSpan[idx] = above;
                                }
                                else
                                {
                                    rowSpan[idx] = leftAbove;
                                }
                            }
                            break;
                        }
                    default:
                        throw new Exception("不支持的算法");
                }
                target.Write(rowSpan);
                Array.Copy(row, lastRow, lastRow.Length);
            }
            return target.ToArray();
        }
    }
}
